/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering,
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 *
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.service.client.impl;

import java.util.Hashtable;
import java.util.Properties;

import cgl.narada.event.EventID;
import cgl.narada.event.NBEvent;
import cgl.narada.event.TemplateInfo;
import cgl.narada.event.impl.NBEventGenerator;
import cgl.narada.service.ServiceException;
import cgl.narada.service.client.ClientService;
import cgl.narada.service.client.EventProducer;
import cgl.narada.service.client.NBNotificationListener;
import cgl.narada.service.client.NBRecoveryListener;
import cgl.narada.service.client.NBRecoveryNotification;
import cgl.narada.service.qos.ProducerConstraints;
import cgl.narada.service.qos.impl.ProducerConstraintsImpl;
import cgl.narada.service.qos.impl.QosServiceImpl;

/**
 * The Event producer implements the interface for the generation of events
 *
 * @author Shrideep Pallickara $Date$ $Revision$
 */

public class EventProducerImpl implements EventProducer {

    private int templateId = 0;
    private int entityId = 0;
    private boolean disableTimestamp = true;
    private boolean ntpTimestamp = false;
    private boolean useHRtimestamp = false;
    private boolean generateEventId = true;
    private boolean routeToSource = false;
    private int timeToLive = 0;
    private int contentSynopsisType = 0;
    private Object contentSynopsis;
    private boolean isTransient = true;
    private boolean isPersistent = false;
    private Properties fragmentationProps, securityProps, compressionProps,
            integrityProps;

    private int priority = 0;
    private int numberOfEventsToBePublished = 0;

    private ClientService clientService;

    private NBEventGenerator eventGenerator;

    private String moduleName = "EventProducerImpl: ";
    private NBNotificationListener nbNotificationListener;

    private QosServiceImpl qosServiceImpl;

    private int producerId;
    private Hashtable recoveryListeners;

    protected EventProducerImpl(int entityId, int producerId,
            QosServiceImpl qosServiceImpl) {
        this.entityId = entityId;
        this.qosServiceImpl = qosServiceImpl;
        this.producerId = producerId;
        eventGenerator = qosServiceImpl.getEventGenerator();
        recoveryListeners = new Hashtable();
    }

    /** Set the template identifier */
    public void setTemplateId(int templateId) {
        this.templateId = templateId;
    }

    /** Disables the generation of timestamps on the event */
    public void setDisableTimestamp(boolean disableTimestamp) {
        this.disableTimestamp = disableTimestamp;
    }

    /**
     * Enable or disable the generation of NTP timestamps for messages. The
     * default is false. Note that this feature may not work depending on the
     * location of your firewall or the availability of NTP servers in your
     * country/continent.
     */
    public void setNTPTimestamp(boolean useNtpTimestamp) {
        this.ntpTimestamp = useNtpTimestamp;
    }

    /**
     * Enable or disable the use of high-resolution timers for timestamps. This
     * is especially true on systems such as NT, XP where the Java system call
     * returns a time that can be off by several milliseconds. Any client that
     * uses this needs to ensure that the ServiceConfiguration.txt is
     * appropriately specified with right setting for the NB_HOME variable
     */
    public void setHighResolutionTimestamp(boolean useHRtimestamp) {
        this.useHRtimestamp = useHRtimestamp;
    }

    /** Enable the generation of event identifiers for published events */
    public void generateEventIdentifier(boolean generateEventId) {
        this.generateEventId = generateEventId;
    }

    /** Prevents the redistribution of an event back to its source */
    public void setSuppressRedistributionToSource(boolean routeToSource) {
        this.routeToSource = routeToSource;
    }

    /** Set the time to live */
    public void setTimeToLive(int timeToLive) {
        this.timeToLive = timeToLive;
    }

    /** Sets the producer to generate persistent events */
    public void setPersistent(boolean isPersistent) {
        this.isPersistent = isPersistent;
        if (isPersistent) {
            isTransient = false;
        }
    }

    /** Sets the producer to generate transient events */
    public void setTransient(boolean isTransient) {
        this.isTransient = isTransient;
        if (isTransient) {
            isPersistent = false;
        }
    }

    /** Sets the priority of the event */
    public void setPriority(int priority) {
        this.priority = priority;
    }

    /** Set the fragmentation parameters */
    public void setFragmentationParameters(Properties props) {
        fragmentationProps = props;
    }

    /** Set the security parameters */
    public void setSecurityParameters(Properties props) {
        securityProps = props;
    }

    /** Set the compression parameters */
    public void setCompressionParameters(Properties props) {
        compressionProps = props;
    }

    /** Set the integrity parameters */
    public void setIntegrityParameters(Properties props) {
        integrityProps = props;
    }

    /** Set the content synopsis information */
    public void setContentSynopsisInfo(int contentSynopsisType,
            Object contentSynopsis) {
        this.contentSynopsisType = contentSynopsisType;
        this.contentSynopsis = contentSynopsis;
    }

    /** Generate NBEvent based on the specified information */
    public NBEvent generateEvent(byte[] payload) throws ServiceException {
        boolean timestamp = true;
        if (disableTimestamp)
            timestamp = false;

        return generateEvent(templateId, entityId, routeToSource, timestamp,
                useHRtimestamp, ntpTimestamp, timeToLive, generateEventId,
                false, null, isPersistent, priority, contentSynopsisType,
                contentSynopsis, payload);

    }

    /** Generate NBEvent based on the specified information */
    public NBEvent generateEvent(int contentSynopsisType,
            Object contentSynopsis, byte[] payload) throws ServiceException {
        boolean timestamp = true;
        if (disableTimestamp)
            timestamp = false;

        return generateEvent(templateId, entityId, routeToSource, timestamp,
                useHRtimestamp, ntpTimestamp, timeToLive, generateEventId,
                false, null, isPersistent, priority, contentSynopsisType,
                contentSynopsis, payload);
    }

    /** Generate NBEvent based on the specified information */
    public NBEvent generateEvent(int contentSynopsisType,
            Object contentSynopsis, EventID correlationId, byte[] payload)
            throws ServiceException {
        boolean timestamp = true;
        if (disableTimestamp)
            timestamp = false;

        return generateEvent(templateId, entityId, routeToSource, timestamp,
                useHRtimestamp, ntpTimestamp, timeToLive, generateEventId,
                false, null, isPersistent, priority, contentSynopsisType,
                contentSynopsis, payload);

    }

    /** Generate NBEvent based on the specified information */
    public NBEvent generateEvent(int contentSynopsisType,
                  Object contentSynopsis, int templateId, byte[] payload)
      throws ServiceException{
      boolean timestamp = true;
      if (disableTimestamp)
        timestamp = false;

      return generateEvent(templateId, entityId, routeToSource,
            timestamp, useHRtimestamp, ntpTimestamp,
            timeToLive, generateEventId,
            false, null,
            isPersistent, priority,
            contentSynopsisType, contentSynopsis,
            payload);
  }

    private NBEvent generateEvent(int templateId, int source,
            boolean suppressDistToSource, boolean timestamp,
            boolean useHRtimestamp, boolean useNtpTimestamp, int timeToLive,
            boolean generateId, boolean isCorrelated, EventID correlationId,
            boolean persistent, int priority, int contentSynopsisType,
            Object contentSynopsis, byte[] contentPayload)
            throws ServiceException {
        return eventGenerator.generateEvent(templateId, source,
                suppressDistToSource, timestamp, useHRtimestamp,
                useNtpTimestamp, timeToLive, generateId, isCorrelated,
                correlationId, persistent, priority, contentSynopsisType,
                contentSynopsis, contentPayload);
    }

    /** Get the template identifier */
    public int getTemplateId() {
        return templateId;
    }

    /** Get the entity identifier */
    public int getEntityId() {
        return entityId;
    }

    /** Determine if generation of timestamps is disabled on the event */
    public boolean getDisableTimestamp() {
        return disableTimestamp;
    }

    /** Determine if the generation of NTP timestamps has been enabled. */
    public boolean getNTPTimestamp() {
        return ntpTimestamp;
    }

    /** Checks if redistribution of an event back to its source is specified */
    public boolean getSuppressRedistributionToSource() {
        return routeToSource;
    }

    /** Get the time to live */
    public int getTimeToLive() {
        return timeToLive;
    }

    /** Checks if producer generate persistent events */
    public boolean isPersistent() {
        return isPersistent;
    }

    /** Checks if producer generate transient events */
    public boolean isTransient() {
        return isTransient;
    }

    /** gets the priority of the generated events */
    public int getPriority() {
        return priority;
    }

    /** Get the fragmentation parameters */
    public Properties getFragmentationParameters() {
        return fragmentationProps;
    }

    /** Get the security parameters */
    public Properties getSecurityParameters() {
        return securityProps;
    }

    /** Get the compression parameters */
    public Properties getCompressionParameters() {
        return compressionProps;
    }

    /** Set the integrity parameters */
    public Properties getIntegrityParameters() {
        return integrityProps;
    }

    /** Get the content synopsis type */
    public int getContentSynopsisType() {
        return contentSynopsisType;
    }

    /** Get the content synopsis */
    public Object getContentSynopsis() {
        return contentSynopsis;
    }

    /** Close the Event Producer */
    public void close() {

    }

    /** Sets the notification listener for this producer */
    public void setNBNotificationListener(
            NBNotificationListener nbNotificationListener) {
        this.nbNotificationListener = nbNotificationListener;
    }

    /**
     * Create producer constraints associated with a certain template
     */
    public ProducerConstraints createProducerConstraints(
            TemplateInfo templateInfo) throws ServiceException {
        if (templateInfo == null) {
            throw new ServiceException(moduleName
                    + "Specified template info is null");
        }

        ProducerConstraints producerConstraints = new ProducerConstraintsImpl(entityId, producerId,
                templateInfo);
        return producerConstraints;
    }

    /** Publish an event */
    public void publishEvent(NBEvent nbEvent) throws ServiceException {
        if (nbEvent == null) {
            throw new ServiceException(moduleName
                    + "Trying to publish NULL event");
        }

        qosServiceImpl.publishEvent(nbEvent, null, producerId);
    }

    /** Publish an event based on the specified Qos constraints */
    public void publishEvent(NBEvent nbEvent,
            ProducerConstraints producerConstraints) throws ServiceException {
        if (nbEvent == null) {
            throw new ServiceException(moduleName
                    + "Trying to publish NULL event");
        }

        if (producerConstraints == null) {
            throw new ServiceException(moduleName
                    + "ProducerConstraints == NULL");
        }

        qosServiceImpl.publishEvent(nbEvent, producerConstraints, producerId);
    }

    /** CHeck if there are pending events to be published */
    public boolean hasEventsToBePublished() {
        if (numberOfEventsToBePublished > 0) {
            return true;
        }
        return false;
    }

    /** Check number of events to be published */
    public int getNumberOfEventsToBePublished() {
        return numberOfEventsToBePublished;
    }

    /**
     * This is called by an entity to recover from failures or prolonged
     * disconnects
     */
    public long recover(int templateId, NBRecoveryListener recoveryListener)
            throws ServiceException {
        if (recoveryListener == null) {
            throw new ServiceException(moduleName
                    + "Specified recoveryListener is NULL");
        }

        if (templateId == 0) {
            throw new ServiceException(moduleName
                    + "Wrong templateId=0 specified for recovery");
        }
        long recoveryId = qosServiceImpl.recoverEntity(producerId, entityId,
                templateId, true);
        Object recoveryKey = new Long(recoveryId);
        recoveryListeners.put(recoveryKey, recoveryListener);

        return recoveryId;
    }

    public void propagateRecoveryNotification(
            NBRecoveryNotification recoveryNotification) {
        long recoveryId = recoveryNotification.getRecoveryId();
        Object recoveryKey = new Long(recoveryId);

        NBRecoveryListener recoveryListener = (NBRecoveryListener) recoveryListeners
                .get(recoveryKey);

        if (recoveryListener == null) {
            System.out.println(moduleName + "RecoveryListener associated with "
                    + recoveryNotification + " is NULL");
            return;
        }

        recoveryListener.onRecovery(recoveryNotification);
    }

}
